package com.meiyuetao.myt.sale.entity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Transient;

import lab.s2jh.bpm.BpmTrackable;
import lab.s2jh.core.annotation.MetaData;
import lab.s2jh.core.util.DateUtils;
import lab.s2jh.core.web.json.DateJsonSerializer;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Formula;
import org.hibernate.envers.NotAudited;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.meiyuetao.myt.core.entity.MytBaseEntity;
import com.meiyuetao.myt.customer.entity.CustomerAccountHist;
import com.meiyuetao.myt.customer.entity.CustomerProfile;
import com.meiyuetao.myt.customer.entity.Score;
import com.meiyuetao.myt.md.entity.Box;

@MetaData("销售订单行项")
@Entity
@Table(name = "iyb_box_order_detail")
@Cache(usage = CacheConcurrencyStrategy.NONE)
public class BoxOrderDetail extends MytBaseEntity implements BpmTrackable, Comparable<BoxOrderDetail> {
    private static final long serialVersionUID = 1L;
    @MetaData("行项流水号，100、110、120……")
    private String sn;
    @MetaData("支付宝接口唯一编号")
    private String orderDetailSeq;
    @MetaData("关联订单")
    private BoxOrder boxOrder;
    @MetaData("盒子名")
    private String boxTitle;
    @MetaData("价格")
    private BigDecimal price = BigDecimal.ZERO;
    @MetaData("数量")
    private Integer quantity = 0;
    @MetaData("发货数量")
    private Integer deliveriedQuantity = 0;
    @MetaData("预付发货时间")
    private Date reserveDeliveryTime;
    @MetaData("客户")
    private CustomerProfile customerProfile;
    @MetaData("付款模式")
    private String payMode;
    @MetaData("付款凭证")
    private String payVoucher;
    @MetaData("上传支付网关号")
    private String detailOutTradeNo;
    @MetaData("行项付款金额")
    private BigDecimal payAmount = BigDecimal.ZERO;
    @MetaData("已退款金额")
    private BigDecimal refundAmount = BigDecimal.ZERO;
    @MetaData("行项付款时间")
    private Date payTime;
    @MetaData("确认付款时间")
    private Date payConfirmTime;
    @MetaData("行项付款状态")
    private BoxOrderDetailStatusEnum orderDetailStatus;
    @MetaData("盒子快照商品列表")
    private List<BoxOrderDetailCommodity> boxOrderDetailCommodities = new ArrayList<BoxOrderDetailCommodity>();
    @MetaData("发货完成时间(后台在所有订单行项发货完成后更新设置)")
    private Date deliveryFinishTime;
    @MetaData("自动收货完成时间(后台用户可修改从而延长收货时间)")
    private Date autoConfirmTime;
    @MetaData("实际收货完成时间(可能是前端用户点击收货完成更新，也可能是定时任务更新)")
    private Date actualConfirmTime;
    @MetaData("实际收货确认操作者(前端用户写入客户唯一标识，后台任务处理写入'SYS')")
    private String actualConfirmOperator;
    @MetaData("自动完结订单时间(后台用户可修改从而延长完结时间)")
    private Date autoSuccessTime;
    @MetaData("实际完结订单时间 (可能是后台用户点击提取完结，也可能是定时任务更新)")
    private Date actualSuccessTime;
    @MetaData("实际订单完结操作者(后台用户写入用户唯一标识，后台任务处理写入'SYS')")
    private String actualSuccessOperator;
    @MetaData("邮费")
    private BigDecimal postage = BigDecimal.ZERO;
    @MetaData("行项关联商品类型")
    private CommoditiesTypeEnum commoditiesType;
    @MetaData("行项里商品是否出现过退货")
    private Boolean hasReturnLog = false;
    @MetaData("")
    private Integer notifiedCount = 0;
    @MetaData("关联盒子")
    private Box box;
    @MetaData("当前工作流活动任务")
    private String activeTaskName;
    @MetaData(value = "原价金额")
    private BigDecimal originalAmount = BigDecimal.ZERO;
    @MetaData(value = "销售(成本)费用")
    private BigDecimal costAmount = BigDecimal.ZERO;
    @MetaData(value = "销售(运费)费用")
    private BigDecimal deliveryAmount = BigDecimal.ZERO;
    @MetaData(value = "商品金额")
    private BigDecimal commodityAmount = BigDecimal.ZERO;
    @MetaData(value = "销售折扣金额")
    private BigDecimal discountAmount = BigDecimal.ZERO;
    @MetaData(value = "实际应付款金额")
    private BigDecimal actualAmount = BigDecimal.ZERO;
    @MetaData("发货物流公司")
    private String logisticsName;
    @MetaData("发货物流单号")
    private String logisticsNo;
    @MetaData("改价备注")
    private String modifierMemo;
    @MetaData(value = "预约发货月")
    private String reserveDeliveryMonth;
    @MetaData("操作")
    private String operationEvent;
    @MetaData("关联客户收支信息")
    private List<CustomerAccountHist> customerAccountHists = new ArrayList<CustomerAccountHist>();
    @MetaData("关联积分")
    private List<Score> scores = new ArrayList<Score>();
    @MetaData("是否已导入网店管家")
    private Boolean exportToEs = Boolean.FALSE;
    @MetaData("是否现货")
    private Boolean buyNow;
    @MetaData("商品编码")
    private String ficno;

    public enum BoxOrderDetailStatusEnum {

        @MetaData("用户已下单")
        S10O,

        @MetaData("计划进行中")
        S15O,

        @MetaData("订单已付款")
        S20PYD,

        @MetaData("行项已付款")
        S25PYD,

        @MetaData("订单已确认")
        S30C,

        @MetaData("行项已确认")
        S35C,

        @MetaData("部分发货")
        S40DP,

        @MetaData("全部发货")
        S50DF,

        @MetaData("用户收货完成")
        S60R,

        @MetaData("订单行项完成")
        S70CLS,

        @MetaData("订单行项取消")
        S90CANCLE,

    }

    public static enum BoxOrderDetailOperationEventEnum {

        MODIFY("订单修改"), CONFIRM("付款确认"), SGC("订单发货"), ADMINMEMO("备注修改"), RECVCFM("收货完成"), CLOSE("订单完结"), INDE("订单调价"), CANCLE("取消订单");

        private String label;

        BoxOrderDetailOperationEventEnum(String label) {
            this.label = label;
        }

        public String getLabel() {
            return this.label;
        }
    }

    public static enum CommoditiesTypeEnum {

        GOODS("普通商品"), SERVICE("服务"), COMBINE("组合");

        private String label;

        CommoditiesTypeEnum(String label) {
            this.label = label;
        }

        public String getLabel() {
            return this.label;
        }
    }

    @ManyToOne
    @JoinColumn(name = "BOX_SID", nullable = false)
    @JsonProperty
    public Box getBox() {
        return box;
    }

    public void setBox(Box box) {
        this.box = box;
    }

    @Column(length = 128)
    public String getBoxTitle() {
        return boxTitle;
    }

    public void setBoxTitle(String boxTitle) {
        this.boxTitle = boxTitle;
    }

    @Column(precision = 18, scale = 2)
    @JsonProperty
    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    @ManyToOne
    @JoinColumn(name = "ORDER_SID", nullable = false)
    @JsonProperty
    public BoxOrder getBoxOrder() {
        return boxOrder;
    }

    public void setBoxOrder(BoxOrder boxOrder) {
        this.boxOrder = boxOrder;
    }

    @JsonProperty
    public Integer getQuantity() {
        return quantity;
    }

    public void setQuantity(Integer quantity) {
        this.quantity = quantity;
    }

    @JsonProperty
    @JsonSerialize(using = DateJsonSerializer.class)
    public Date getReserveDeliveryTime() {
        return reserveDeliveryTime;
    }

    public void setReserveDeliveryTime(Date reserveDeliveryTime) {
        this.reserveDeliveryTime = reserveDeliveryTime;
    }

    @JsonProperty
    public Integer getDeliveriedQuantity() {
        return deliveriedQuantity;
    }

    public void setDeliveriedQuantity(Integer deliveriedQuantity) {
        this.deliveriedQuantity = deliveriedQuantity;
    }

    @OneToOne
    @JoinColumn(name = "Customer_Profile_Sid", nullable = false)
    @NotAudited
    public CustomerProfile getCustomerProfile() {
        return customerProfile;
    }

    public void setCustomerProfile(CustomerProfile customerProfile) {
        this.customerProfile = customerProfile;
    }

    @JsonProperty
    @Column(precision = 18, scale = 2)
    public BigDecimal getPayAmount() {
        return payAmount;
    }

    public void setPayAmount(BigDecimal payAmount) {
        this.payAmount = payAmount;
    }

    public Date getPayTime() {
        return payTime;
    }

    public void setPayTime(Date payTime) {
        this.payTime = payTime;
    }

    @Column(length = 64)
    public String getOrderDetailSeq() {
        return orderDetailSeq;
    }

    public void setOrderDetailSeq(String orderDetailSeq) {
        this.orderDetailSeq = orderDetailSeq;
    }

    @Enumerated(EnumType.STRING)
    @Column(length = 8)
    @JsonProperty
    public BoxOrderDetailStatusEnum getOrderDetailStatus() {
        return orderDetailStatus;
    }

    public void setOrderDetailStatus(BoxOrderDetailStatusEnum orderDetailStatus) {
        this.orderDetailStatus = orderDetailStatus;
    }

    public Date getAutoConfirmTime() {
        return autoConfirmTime;
    }

    public void setAutoConfirmTime(Date autoConfirmTime) {
        this.autoConfirmTime = autoConfirmTime;
    }

    public Date getDeliveryFinishTime() {
        return deliveryFinishTime;
    }

    public void setDeliveryFinishTime(Date deliveryFinishTime) {
        this.deliveryFinishTime = deliveryFinishTime;
    }

    public Date getActualConfirmTime() {
        return actualConfirmTime;
    }

    public void setActualConfirmTime(Date actualConfirmTime) {
        this.actualConfirmTime = actualConfirmTime;
    }

    @Column(length = 128)
    public String getActualConfirmOperator() {
        return actualConfirmOperator;
    }

    public void setActualConfirmOperator(String actualConfirmOperator) {
        this.actualConfirmOperator = actualConfirmOperator;
    }

    @OneToMany(mappedBy = "boxOrderDetail", cascade = CascadeType.ALL, orphanRemoval = true)
    @NotAudited
    public List<BoxOrderDetailCommodity> getBoxOrderDetailCommodities() {
        return boxOrderDetailCommodities;
    }

    public void setBoxOrderDetailCommodities(List<BoxOrderDetailCommodity> boxOrderDetailCommodities) {
        this.boxOrderDetailCommodities = boxOrderDetailCommodities;
    }

    @Column(length = 16)
    @JsonProperty
    public String getSn() {
        return sn;
    }

    public void setSn(String sn) {
        this.sn = sn;
    }

    public Date getAutoSuccessTime() {
        return autoSuccessTime;
    }

    public void setAutoSuccessTime(Date autoSuccessTime) {
        this.autoSuccessTime = autoSuccessTime;
    }

    public Date getActualSuccessTime() {
        return actualSuccessTime;
    }

    public void setActualSuccessTime(Date actualSuccessTime) {
        this.actualSuccessTime = actualSuccessTime;
    }

    @Column(length = 128)
    public String getActualSuccessOperator() {
        return actualSuccessOperator;
    }

    public void setActualSuccessOperator(String actualSuccessOperator) {
        this.actualSuccessOperator = actualSuccessOperator;
    }

    public Date getPayConfirmTime() {
        return payConfirmTime;
    }

    public void setPayConfirmTime(Date payConfirmTime) {
        this.payConfirmTime = payConfirmTime;
    }

    @Column(length = 18, nullable = false)
    public BigDecimal getPostage() {
        return postage;
    }

    public void setPostage(BigDecimal postage) {
        this.postage = postage;
    }

    @Column(length = 512)
    public String getPayVoucher() {
        return payVoucher;
    }

    public void setPayVoucher(String payVoucher) {
        this.payVoucher = payVoucher;
    }

    @Column(length = 16)
    public String getPayMode() {
        return payMode;
    }

    public void setPayMode(String payMode) {
        this.payMode = payMode;
    }

    @Column(precision = 18, scale = 2)
    public BigDecimal getRefundAmount() {
        return refundAmount;
    }

    public void setRefundAmount(BigDecimal refundAmount) {
        this.refundAmount = refundAmount;
    }

    @Column(length = 64)
    public String getDetailOutTradeNo() {
        return detailOutTradeNo;
    }

    public void setDetailOutTradeNo(String detailOutTradeNo) {
        this.detailOutTradeNo = detailOutTradeNo;
    }

    @Enumerated(EnumType.STRING)
    @Column(length = 64)
    public CommoditiesTypeEnum getCommoditiesType() {
        return commoditiesType;
    }

    public void setCommoditiesType(CommoditiesTypeEnum commoditiesType) {
        this.commoditiesType = commoditiesType;
    }

    public Boolean getHasReturnLog() {
        return hasReturnLog;
    }

    public void setHasReturnLog(Boolean hasReturnLog) {
        this.hasReturnLog = hasReturnLog;
    }

    public Integer getNotifiedCount() {
        return notifiedCount;
    }

    public void setNotifiedCount(Integer notifiedCount) {
        this.notifiedCount = notifiedCount;
    }

    @JsonProperty
    public BigDecimal getOriginalAmount() {
        return originalAmount;
    }

    public void setOriginalAmount(BigDecimal originalAmount) {
        this.originalAmount = originalAmount;
    }

    @Transient
    @JsonProperty
    public BigDecimal getAmount() {
        return price.multiply(new BigDecimal(quantity));
    }

    @JsonProperty
    public String getActiveTaskName() {
        return activeTaskName;
    }

    public void setActiveTaskName(String activeTaskName) {
        this.activeTaskName = activeTaskName;
    }

    @Transient
    public String getBizKey() {
        return boxOrder.getOrderSeq() + "-" + sn;
    }

    @JsonProperty
    public BigDecimal getDiscountAmount() {
        return discountAmount;
    }

    public void setDiscountAmount(BigDecimal discountAmount) {
        this.discountAmount = discountAmount;
    }

    @JsonProperty
    public BigDecimal getActualAmount() {
        return actualAmount;
    }

    public void setActualAmount(BigDecimal actualAmount) {
        this.actualAmount = actualAmount;
    }

    public BigDecimal getCostAmount() {
        return costAmount;
    }

    public void setCostAmount(BigDecimal costAmount) {
        this.costAmount = costAmount;
    }

    public BigDecimal getDeliveryAmount() {
        return deliveryAmount;
    }

    public void setDeliveryAmount(BigDecimal deliveryAmount) {
        this.deliveryAmount = deliveryAmount;
    }

    public BigDecimal getCommodityAmount() {
        return commodityAmount;
    }

    public void setCommodityAmount(BigDecimal commodityAmount) {
        this.commodityAmount = commodityAmount;
    }

    public String getLogisticsName() {
        return logisticsName;
    }

    public void setLogisticsName(String logisticsName) {
        this.logisticsName = logisticsName;
    }

    public String getLogisticsNo() {
        return logisticsNo;
    }

    public void setLogisticsNo(String logisticsNo) {
        this.logisticsNo = logisticsNo;
    }

    @Override
    @Transient
    @JsonIgnore
    public String getBpmBusinessKey() {
        return boxOrder.getOrderSeq() + "-" + sn;
    }

    @Transient
    @JsonIgnore
    public String getVoucher() {
        return boxOrder.getOrderSeq() + "-" + sn;
    }

    @Column(length = 2000)
    @JsonProperty
    public String getModifierMemo() {
        return modifierMemo;
    }

    public void setModifierMemo(String modifierMemo) {
        this.modifierMemo = modifierMemo;
    }

    @Formula("(CONVERT(varchar(7), reserve_delivery_time, 120))")
    @JsonProperty
    @NotAudited
    public String getReserveDeliveryMonth() {
        return reserveDeliveryMonth;
    }

    public void setReserveDeliveryMonth(String reserveDeliveryMonth) {
        this.reserveDeliveryMonth = reserveDeliveryMonth;
    }

    @Override
    @Transient
    public String getDisplay() {
        return sn + " " + boxTitle;
    }

    @Column(length = 4000)
    public String getOperationEvent() {
        return operationEvent;
    }

    public void setOperationEvent(String operationEvent) {
        this.operationEvent = operationEvent;
    }

    public void addOperationEvent(String operation, String userPin) {
        this.operationEvent = (operationEvent == null ? "" : operationEvent) + operation + "," + userPin + "," + DateUtils.formatTime(new Date()) + ";";

    }

    @OneToMany(mappedBy = "boxOrderDetail", cascade = CascadeType.ALL, orphanRemoval = true)
    @NotAudited
    public List<CustomerAccountHist> getCustomerAccountHists() {
        return customerAccountHists;
    }

    public void setCustomerAccountHists(List<CustomerAccountHist> customerAccountHists) {
        this.customerAccountHists = customerAccountHists;
    }

    @OneToMany(mappedBy = "boxOrderDetail", cascade = CascadeType.ALL, orphanRemoval = true)
    @NotAudited
    public List<Score> getScores() {
        return scores;
    }

    public void setScores(List<Score> scores) {
        this.scores = scores;
    }

    @Override
    @Transient
    public String getExtraInfo() {

        return null;
    }

    @Column(name = "is_export_to_es")
    public Boolean getExportToEs() {
        return exportToEs;
    }

    public void setExportToEs(Boolean exportToEs) {
        this.exportToEs = exportToEs;
    }

    @Override
    public int compareTo(BoxOrderDetail o) {
        if (this.reserveDeliveryTime.before(o.getReserveDeliveryTime())) {
            return 1;
        } else if (this.reserveDeliveryTime.after(o.getReserveDeliveryTime())) {
            return -1;
        } else {
            return 0;
        }
    }

    @Column(name = "buy_now")
    public Boolean getBuyNow() {
        return buyNow;
    }

    public void setBuyNow(Boolean buyNow) {
        this.buyNow = buyNow;
    }

    public String getFicno() {
        return ficno;
    }

    public void setFicno(String ficno) {
        this.ficno = ficno;
    }

}
